/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2011-3-12     Yi Qiu      first version
 */

#ifndef __RT_USB_HOST_H__
#define __RT_USB_HOST_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <rtthread.h>
#include "usb/usb_common.h"

#include <stdint.h>

#define USB_MAX_DEVICE                  0x20
#define USB_MAX_INTERFACE               0x08
#define USB_HUB_PORT_NUM                0x04
#define SIZEOF_USB_REQUEST              0x08

#define DEV_STATUS_IDLE                 0x00
#define DEV_STATUS_BUSY                 0x01
#define DEV_STATUS_ERROR                0x02

#define UPIPE_STATUS_OK                 0x00
#define UPIPE_STATUS_STALL              0x01
#define UPIPE_STATUS_ERROR              0x02

#define USBH_PID_SETUP                  0x00
#define USBH_PID_DATA                   0x01

struct uhcd;
struct uhintf;
struct uhub;
struct upipe;
struct urb;

struct udevice_match
{
    uint16_t flags;

	/* Used for product specific matches; range is inclusive */
	uint16_t idVendor;
	uint16_t idProduct;
	uint16_t bcdDevice_lo;
	uint16_t bcdDevice_hi;

	/* Used for device class matches */
	uint8_t bDeviceClass;
	uint8_t bDeviceSubClass;
	uint8_t bDeviceProtocol;

	/* Used for interface class matches */
	uint8_t bInterfaceClass;
	uint8_t bInterfaceSubClass;
	uint8_t bInterfaceProtocol;
};

/* Some useful macros to use to create struct usb_device_id */
#define USB_DEVICE_ID_MATCH_VENDOR		0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT		0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO		0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI		0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS		0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS	0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL	0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS		0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS	0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL	0x0200
#define USB_DEVICE_ID_MATCH_INT_NUMBER		0x0400

#define USB_DEVICE_ID_MATCH_DEVICE \
		(USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
#define USB_DEVICE_ID_MATCH_DEV_RANGE \
		(USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)
#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION \
		(USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)
#define USB_DEVICE_ID_MATCH_DEV_INFO \
		(USB_DEVICE_ID_MATCH_DEV_CLASS | \
		USB_DEVICE_ID_MATCH_DEV_SUBCLASS | \
		USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
#define USB_DEVICE_ID_MATCH_INT_INFO \
		(USB_DEVICE_ID_MATCH_INT_CLASS | \
		USB_DEVICE_ID_MATCH_INT_SUBCLASS | \
		USB_DEVICE_ID_MATCH_INT_PROTOCOL)

#define USB_INTERFACE_MATCH(cl, sc, pr) \
	.flags = USB_DEVICE_ID_MATCH_INT_INFO, \
	.bInterfaceClass = (cl), \
	.bInterfaceSubClass = (sc), \
	.bInterfaceProtocol = (pr)

struct uclass_driver
{
    rt_list_t list;
    const struct udevice_match *match;

    rt_err_t (*enable)(void* arg);
    rt_err_t (*disable)(void* arg);

    unsigned int minors;
    void* user_data;
};
typedef struct uclass_driver* ucd_t;

struct uprotocal
{
    rt_list_t list;
    int pro_id;
    
    rt_err_t (*init)(void* arg);
    rt_err_t (*callback)(void* arg);    
};
typedef struct uprotocal* uprotocal_t;

struct uinstance
{
    struct rt_device parent;

    struct udevice_descriptor dev_desc;
    ucfg_desc_t cfg_desc;
    struct uhcd *hcd;

    struct upipe * pipe_ep0_out;
    struct upipe * pipe_ep0_in;
    rt_list_t pipe;

    rt_uint8_t status;
    rt_uint8_t type;
    rt_uint8_t index;
    rt_uint8_t address;    
    rt_uint8_t speed;
    rt_uint8_t max_packet_size;    
    rt_uint8_t port;

    struct uhub* parent_hub;
    struct uhintf* intf[USB_MAX_INTERFACE];        
};
typedef struct uinstance* uinst_t;

struct uhintf
{
    struct uinstance* device;
    uintf_desc_t intf_desc;

    ucd_t drv;
    void *user_data;
};

struct upipe
{
    rt_list_t list;
    uint16_t pipe_index;
    int16_t status;
    struct uendpoint_descriptor ep;
    uinst_t inst;
    func_callback callback;
    rt_list_t urbhead;
    rt_list_t urbs;
    rt_list_t urbidle;
    void* user_data;
};
typedef struct upipe* upipe_t;

struct uhub
{
    struct uhub_descriptor hub_desc;
    rt_uint8_t num_ports;
    rt_uint32_t port_status[USB_HUB_PORT_NUM];
    struct uinstance* child[USB_HUB_PORT_NUM];        

    rt_bool_t is_roothub;

    rt_uint8_t buffer[8];    
    struct uinstance* self;
    struct uhcd *hcd;
};
typedef struct uhub* uhub_t;

typedef void (*urbcomp_t)(struct urb *r);

struct urb
{
    rt_list_t link;
    uint8_t *buf;
    uint16_t size;
    uint16_t actual_size;
    uint16_t bufsize;
    short status;
    urbcomp_t comp_cb;
    uint8_t is_setup;
    uint8_t is_working;
    uint16_t pos;

    rt_list_t list;

    void *context;
};

struct uhcd_ops
{
    int (*start)(struct uhcd *hcd);
    void (*stop)(struct uhcd *hcd);

    int (*reset_port)(struct uhcd *hcd, rt_uint8_t port);
    int (*pipe_xfer) (struct uhcd *hcd, upipe_t pipe, rt_uint8_t token, void* buffer, int nbytes, int timeout);
    int (*open_pipe) (struct uhcd *hcd, upipe_t pipe);
    int (*close_pipe)(struct uhcd *hcd, upipe_t pipe);

    int (*urb_enqueue)(struct uhcd *hcd, struct upipe *pipe, struct urb *r);
    void (*urb_dequeue)(struct uhcd *hcd, struct upipe *pipe, struct urb *r);
};
typedef struct uhcd_ops* uhcd_ops_t;

struct uhcd
{
    struct rt_device parent;

    const struct uhcd_ops *ops;
    rt_uint8_t num_ports;
    uhub_t roothub; 
};
typedef struct uhcd* uhcd_t;

enum uhost_msg_type
{
    USB_MSG_CONNECT_CHANGE,
    USB_MSG_CALLBACK,
};
typedef enum uhost_msg_type uhost_msg_type;

struct uhost_msg
{
    uhost_msg_type type; 
    union
    {
        struct uhub* hub;
        struct 
        {
            func_callback function;
            void *context;
        }cb;
    }content;
};
typedef struct uhost_msg* uhost_msg_t;

/* usb host system interface */
void rt_usbh_hub_init(struct uhcd *hcd);

/* usb host core interface */
struct uinstance* rt_usbh_alloc_instance(uhcd_t uhcd);
rt_err_t rt_usbh_attatch_instance(struct uinstance* device);
rt_err_t rt_usbh_detach_instance(struct uinstance* device);
rt_err_t rt_usbh_get_descriptor(struct uinstance* device, rt_uint8_t type, void* buffer, int nbytes);
rt_err_t rt_usbh_set_configure(struct uinstance* device, int config);
rt_err_t rt_usbh_set_address(struct uinstance* device);
rt_err_t rt_usbh_set_interface(struct uinstance* device, int intf);
rt_err_t rt_usbh_clear_feature(struct uinstance* device, int endpoint, int feature);
rt_err_t rt_usbh_get_interface_descriptor(ucfg_desc_t cfg_desc, int num, uintf_desc_t* intf_desc);
rt_err_t rt_usbh_get_endpoint_descriptor(uintf_desc_t intf_desc, int num, uep_desc_t* ep_desc);

int rt_usbh_cleanup_submit(void (*cleanup)(void*), void *usrdata);

struct urb* rt_usbh_urb_alloc(unsigned bufsize, urbcomp_t cb);
int rt_usbh_urb_submit(struct uinstance *dev, struct upipe *pipe, struct urb *r);
int rt_usbh_control_msg(struct uinstance *dev, struct urequest *setup, void *data, int size, int timeout);
int rt_usbh_urb_killall(struct uinstance *dev, struct upipe *pipe);
int rt_usbh_urb_allocall(struct upipe *pipe, unsigned bufsize, urbcomp_t cb, void *context, int n);
int rt_usbh_urb_freeall(struct uinstance *dev, struct upipe *pipe);
struct urb* rt_usbh_urb_idle_take(struct upipe *pipe);

/* usb class driver interface */
rt_err_t rt_usbh_class_driver_init(void);
rt_err_t rt_usbh_class_driver_register(ucd_t drv);
rt_err_t rt_usbh_class_driver_unregister(ucd_t drv);
rt_err_t rt_usbh_class_driver_enable(ucd_t drv, void* args);
rt_err_t rt_usbh_class_driver_disable(ucd_t drv, void* args);
ucd_t rt_usbh_class_driver_find(int class_code, int subclass_code);

/* usb class driver implement */
ucd_t rt_usbh_class_driver_hub(void);
ucd_t rt_usbh_class_driver_storage(void);



/* usb hub interface */
rt_err_t rt_usbh_hub_get_descriptor(struct uinstance* device, rt_uint8_t *buffer, 
    rt_size_t size);
rt_err_t rt_usbh_hub_get_status(struct uinstance* device, rt_uint32_t* buffer);
rt_err_t rt_usbh_hub_get_port_status(uhub_t uhub, rt_uint16_t port, 
    rt_uint32_t* buffer);
rt_err_t rt_usbh_hub_clear_port_feature(uhub_t uhub, rt_uint16_t port, 
    rt_uint16_t feature);
rt_err_t rt_usbh_hub_set_port_feature(uhub_t uhub, rt_uint16_t port, 
    rt_uint16_t feature);
rt_err_t rt_usbh_hub_reset_port(uhub_t uhub, rt_uint16_t port);
rt_err_t rt_usbh_event_signal(struct uhost_msg* msg);


void rt_usbh_root_hub_connect_handler(struct uhcd *hcd, rt_uint8_t port, rt_bool_t isHS);
void rt_usbh_root_hub_disconnect_handler(struct uhcd *hcd, rt_uint8_t port);

/* usb host controller driver interface */
rt_err_t rt_usb_instance_add_pipe(uinst_t inst, upipe_t pipe);
upipe_t rt_usb_instance_find_pipe(uinst_t inst, rt_uint8_t ep_address);
void rt_usb_pipe_add_callback(upipe_t pipe, func_callback callback);

int rt_usb_hcd_start(struct uhcd *hcd);
int rt_usb_hcd_pipe_xfer(uhcd_t hcd, upipe_t pipe, void* buffer, int nbytes, int timeout);
int rt_usb_hcd_setup_xfer(uhcd_t hcd, upipe_t pipe, ureq_t setup, int timeout);
int rt_usb_hcd_free_pipe(uhcd_t hcd, upipe_t pipe);
int rt_usb_hcd_alloc_pipe(uhcd_t hcd, upipe_t* pipe, uinst_t inst, uep_desc_t ep);
int rt_usb_hcd_port_reset(uhcd_t hcd, rt_uint8_t port);
int rt_usb_hcd_urb_kill(struct uhcd *hcd, struct upipe *pipe, struct urb *r);

void rt_usb_hcd_event_urb_complete(struct urb *r, int status);
int rt_usb_hcd_urb_link(struct upipe *pipe, struct urb *r, int isworking);
struct urb* rt_usb_hcd_urb_first(struct upipe *pipe);

#ifdef __cplusplus
}
#endif

#endif


